using System;
using System.Collections.Generic;
using System.IO;
using LiteNetLib;
using UnityEngine;
using K4os.Compression.LZ4;
using Unity.Netcode;

namespace Since.Netcode
{
	public class RelayNetworkTransport : NetworkTransport
	{
		enum ConstVariable
		{
			HostConnected = 10,
			ClientConnected = 11,
			ClientConnectConfirm = 12,
			Data = 13,
			Disconnect = 14,
		}

		// 网络配置
		[Header("网络配置")]
		public		string Address = "127.0.0.1";
        public ushort Port = 9999;
        public ulong LocalClientId = 121212;

		public void SetIpAndPort(string ip, ushort port)
		{
			Address = ip;
			Port = port;
		}


		[Header("高级设置")]
		public int ConnectionTimeout = 5000; // 毫秒

		[Header("调试设置")]
		public bool EnableVerboseLogging = false;
		public bool SimulatePacketLoss = false;
		public int PacketLossChance = 5; // 5%
		public int SimulatedLatencyMs = 0;

		// 内部变量
		private NetworkManager _networkManager;
		private NetManager _netManager;
		private EventBasedNetListener _listener;
		private Dictionary<ulong, string> _clients = new Dictionary<ulong, string>();
		private Dictionary<int, ulong> _peerIdToClientId = new Dictionary<int, ulong>();
		private ulong _nextClientId = 1;
		private bool _isServer;
		private bool _isRunning;
		private Queue<TransportEvent> _eventQueue = new Queue<TransportEvent>();
		private object _eventLock = new object();
		private int _sentPackets = 0;
		private int _receivedPackets = 0;
		private NetPeer _relayServerPeer;

		// 传输事件类
		private class TransportEvent
		{
			public NetworkEvent Type;
			public ulong ClientId;
			public ArraySegment<byte> Data;
			public float Time;
		}

		// ServerClientId属性实现
		public override ulong ServerClientId => 0;

		// Unity生命周期方法
		private void OnDestroy()
		{
			Shutdown();
		}

		// 覆盖Update方法
		public void UpdateEvents()
		{
			// 处理队列中的所有事件
			lock (_eventLock)
			{
				while (_eventQueue.Count > 0)
				{
					var transportEvent = _eventQueue.Dequeue();
					InvokeOnTransportEvent(transportEvent.Type, transportEvent.ClientId,
						transportEvent.Data, transportEvent.Time);
				}
			}
		}

		// 添加Unity的Update方法
		private void Update()
		{
			if (_isRunning && _netManager != null)
			{
				_netManager.PollEvents();
				UpdateEvents();
			}
		}

		// 实现PollEvent抽象方法
		public override NetworkEvent PollEvent(out ulong clientId, out ArraySegment<byte> payload, out float receiveTime)
		{
			// 这个方法在新版本的Netcode中不再主动调用，但我们仍然需要实现它
			clientId = 0;
			payload = new ArraySegment<byte>();
			receiveTime = 0;

			lock (_eventLock)
			{
				if (_eventQueue.Count > 0)
				{
					var transportEvent = _eventQueue.Dequeue();
					clientId = transportEvent.ClientId;
					payload = transportEvent.Data;
					receiveTime = transportEvent.Time;
					return transportEvent.Type;
				}
			}

			return NetworkEvent.Nothing;
		}


		// NetworkTransport必须实现的方法
		public override void Initialize(NetworkManager networkManager = null)
		{
			_networkManager = networkManager;
			LogInfo("自定义网络传输初始化");
			LZ4Codec.Enforce32 = true;
		}

		public override bool StartClient()
		{
			if (_isRunning)
			{
				LogWarning("客户端已在运行，无法再次启动");
				return false;
			}

			try
			{
				_isServer = false;

				// 初始化LiteNetLib
				_listener = new EventBasedNetListener();
				_netManager = new NetManager(_listener);

				// 配置延迟模拟
				if (SimulatedLatencyMs > 0)
				{
					_netManager.SimulateLatency = true;
					_netManager.SimulationMinLatency = (int)SimulatedLatencyMs;
					_netManager.SimulationMaxLatency = (int)SimulatedLatencyMs;
				}

				// 配置丢包模拟
				if (SimulatePacketLoss)
				{
					_netManager.SimulatePacketLoss = true;
					_netManager.SimulationPacketLossChance = PacketLossChance;
				}

				// 设置超时时间
				_netManager.DisconnectTimeout = ConnectionTimeout;

				// 启动客户端
				var isStart = _netManager.Start();
				if (!isStart)
				{
					LogError("启动失败");
					_isRunning = false;
					_netManager.Stop();
					return false;
				}
				// 连接到服务器
				var peer = _netManager.Connect(Address, Port, string.Empty);
				if (peer == null)
				{
					LogError("连接失败");
					_isRunning = false;
					_netManager.Stop();
					return false;
				}
				else
				{
					// 注册事件
					RegisterNetworkEvents();

					LogInfo($"客户端udp已启动,Port:{_netManager.LocalPort}");
					_isRunning = true;
					LogInfo($"客户端已启动并尝试连接到 {Address}:{Port}");

					return true;
				}
			}
			catch (Exception e)
			{
				LogError($"客户端启动失败: {e.Message}");
				return false;
			}
		}

		public override bool StartServer()
		{
			if (_isRunning)
			{
				LogWarning("服务器已在运行，无法再次启动");
				return false;
			}

			try
			{
				_isServer = true;

				// 初始化LiteNetLib
				_listener = new EventBasedNetListener();
				_netManager = new NetManager(_listener);

				// 配置延迟模拟
				if (SimulatedLatencyMs > 0)
				{
					_netManager.SimulateLatency = true;
					_netManager.SimulationMinLatency = (int)SimulatedLatencyMs;
					_netManager.SimulationMaxLatency = (int)SimulatedLatencyMs;
				}

				// 配置丢包模拟
				if (SimulatePacketLoss)
				{
					_netManager.SimulatePacketLoss = true;
					_netManager.SimulationPacketLossChance = PacketLossChance;
				}

				// 设置超时时间和最大连接数
				_netManager.DisconnectTimeout = ConnectionTimeout;


				// 启动服务器
				var isStarted = _netManager.Start();
				if (!isStarted)
				{
					LogError("启动失败");
					_isRunning = false;
					_netManager.Stop();
					return false;
				}
				var peer = _netManager.Connect(Address, Port, string.Empty);
				if (peer == null)
				{
					LogError("连接失败");
					_isRunning = false;
					_netManager.Stop();

					return false;
				}
				else
				{
					_isRunning = true;
					LogInfo($"服务器已启动,Port:{_netManager.LocalPort}");
					// 注册事件
					RegisterNetworkEvents();

					return true;
				}
			}
			catch (Exception e)
			{
				LogError($"服务器启动失败: {e.Message}");
				return false;
			}
		}

		private void RegisterNetworkEvents()
		{
			// 注册连接事件
			_listener.PeerConnectedEvent += OnPeerConnected;
			// 注册断开连接事件
			_listener.PeerDisconnectedEvent += OnPeerDisconnected;
			// 注册接收数据事件
			_listener.NetworkReceiveEvent += OnNetworkReceive;
		}

		private void OnPeerConnected(NetPeer peer)
		{
			try
			{
				//只要连接，就是RelayServer,这时候只保存RelayServer引用，不分配客户端
				_relayServerPeer = peer;

				LogInfo($"已连接到RelayServer (ID: {peer})");

				if (_isServer)
				{
					//通知relayServer服务器已连接
					BinaryWriter binaryWriter = GetBinaryWriter();
					binaryWriter.Write((byte)ConstVariable.HostConnected);
					binaryWriter.Write(ServerClientId);
					var memoryStream = (MemoryStream)binaryWriter.BaseStream;
					var fullData = memoryStream.ToArray();
					ReturnBinaryWriter(binaryWriter);
					// 发送数据到RelayServer
					SendData(fullData,DeliveryMethod.ReliableOrdered);

					LogInfo($"已发送服务器连接通知到RelayServer");
				}
				else
				{
					//通知relayServer客户端已连接
					BinaryWriter binaryWriter = GetBinaryWriter();
					binaryWriter.Write((byte)ConstVariable.ClientConnected);
					binaryWriter.Write(LocalClientId);
					var memoryStream = (MemoryStream)binaryWriter.BaseStream;
					var fullData = memoryStream.ToArray();
					ReturnBinaryWriter(binaryWriter);
					// 发送数据到RelayServer
					SendData(fullData,DeliveryMethod.ReliableOrdered);

					LogInfo($"已发送客户端连接通知到RelayServer");
				}
			}
			catch (Exception e)
			{
				LogError($"处理连接事件时出错: {e.Message}");
			}
		}

		void SendData(byte[] data,DeliveryMethod method)
		{
			// 发送数据到RelayServer
			var zipData = Lz4Helper.CompressWithLength(data);
			_relayServerPeer.Send(zipData, method);
		}

		private void OnPeerDisconnected(NetPeer peer, DisconnectInfo disconnectInfo)
		{
			try
			{
				ulong clientId;

				if (_isServer)
				{
					// 服务器模式：查找断开连接的客户端ID
					if (_peerIdToClientId.TryGetValue(peer.Id, out clientId))
					{
						_clients.Remove(clientId);
						_peerIdToClientId.Remove(peer.Id);

						LogInfo($"客户端 {clientId} (ID: {peer.Id}) 已断开连接，原因: {disconnectInfo.Reason}");
					}
					else
					{
						LogWarning($"收到未知客户端 (ID: {peer.Id}) 的断开连接事件");
						return;
					}
				}
				else
				{
					// 客户端模式：服务器已断开连接
					clientId = ServerClientId;
					_relayServerPeer = null;

					LogInfo($"与服务器 (ID: {peer.Id}) 的连接已断开，原因: {disconnectInfo.Reason}");
				}

				// 通知断开连接事件
				QueueTransportEvent(NetworkEvent.Disconnect, clientId, new ArraySegment<byte>());
			}
			catch (Exception e)
			{
				LogError($"处理断开连接事件时出错: {e.Message}");
			}
		}

		private void OnNetworkReceive(NetPeer peer, NetPacketReader reader, byte channel, DeliveryMethod deliveryMethod)
		{
			try
			{
				_receivedPackets++;

				var regionBytes = reader.GetRemainingBytes();
				var bytes = Lz4Helper.DecompressWithLength(regionBytes);
				var binaryReader = GetBinaryReader();
				binaryReader.BaseStream.Write(bytes, 0, bytes.Length);
				binaryReader.BaseStream.Seek(0, SeekOrigin.Begin);

				var key = binaryReader.ReadByte();
				reader.Recycle();

				if (_isServer)
				{
					if (key == (byte)ConstVariable.ClientConnected)
					{
						LogInfo($"收到客户端连接通知");
						var clientPeerString = binaryReader.ReadString();

						var clientId = _nextClientId++;
						_clients[clientId] = clientPeerString;

						BinaryWriter binaryWriter = GetBinaryWriter();
						binaryWriter.Write((byte)ConstVariable.ClientConnectConfirm);
						binaryWriter.Write(clientId);
						binaryWriter.Write(clientPeerString);
						var memoryStream = (MemoryStream)binaryWriter.BaseStream;
						var fullData = memoryStream.ToArray();
						ReturnBinaryWriter(binaryWriter);
						SendData(fullData,DeliveryMethod.ReliableOrdered);

						QueueTransportEvent(NetworkEvent.Connect, clientId, new ArraySegment<byte>());
					}
					else if (key == (byte)ConstVariable.Data)
					{
						var clientId = binaryReader.ReadUInt64();
						var srcClientId = binaryReader.ReadUInt64();
						// 读取剩余数据
						var remainingBytes = binaryReader.BaseStream.Length - binaryReader.BaseStream.Position;
						var data = binaryReader.ReadBytes((int)remainingBytes);
						QueueTransportEvent(NetworkEvent.Data, srcClientId, new ArraySegment<byte>(data));
					}
					else if (key == (byte)ConstVariable.Disconnect)
					{
						var clientId = binaryReader.ReadUInt64();
						var srcClientId = binaryReader.ReadUInt64();
						LogInfo($"收到客户端断开连接通知: {srcClientId}");
						_clients.Remove(srcClientId);
						QueueTransportEvent(NetworkEvent.Disconnect, srcClientId, new ArraySegment<byte>());
					}
				}
				else
				{
					if (key == (byte)ConstVariable.ClientConnectConfirm)
					{
						LogInfo($"收到服务器连接确认通知:");
						var clientId = binaryReader.ReadUInt64();
						LocalClientId = clientId;

						QueueTransportEvent(NetworkEvent.Connect, clientId, new ArraySegment<byte>());
					}
					else if (key == (byte)ConstVariable.Data)
					{
						var clientId = binaryReader.ReadUInt64();
						var srcClientId = binaryReader.ReadUInt64();
						// 读取剩余数据
						var remainingBytes = binaryReader.BaseStream.Length - binaryReader.BaseStream.Position;
						var data = binaryReader.ReadBytes((int)remainingBytes);
						QueueTransportEvent(NetworkEvent.Data, srcClientId, new ArraySegment<byte>(data));
					}
					else if (key == (byte)ConstVariable.Disconnect)
					{
						var clientId = binaryReader.ReadUInt64();
						var srcClientId = binaryReader.ReadUInt64();
						LogInfo($"收到服务器断开连接通知: {srcClientId}");
						QueueTransportEvent(NetworkEvent.Disconnect, srcClientId, new ArraySegment<byte>());
					}
				}

				ReturnBinaryReader(binaryReader);
			}
			catch (Exception e)
			{
				LogError($"处理接收数据事件时出错: {e.Message}");
				reader.Recycle();
			}
		}

		public override void DisconnectRemoteClient(ulong clientId)
		{
			if (!_isServer)
			{
				LogWarning("只有服务器可以断开远程客户端连接");
				return;
			}

			try
			{
				SendDisconnect(clientId);
			}
			catch (Exception e)
			{
				LogError($"断开远程客户端连接时出错: {e.Message}");
			}
		}

		public override void DisconnectLocalClient()
		{
			if (_isServer)
			{
				LogWarning("服务器不能调用DisconnectLocalClient");
				return;
			}

			try
			{
				if (_relayServerPeer != null)
				{
					LogInfo("客户端主动断开与服务器的连接");

					SendDisconnect(0);
					QueueTransportEvent(NetworkEvent.Disconnect, 0, new ArraySegment<byte>());
				}
				else
				{
					LogWarning("客户端尚未连接到服务器，无法断开连接");
				}
			}
			catch (Exception e)
			{
				LogError($"断开本地客户端连接时出错: {e.Message}");
			}
		}

		void SendDisconnect(ulong clientId)
		{
			BinaryWriter binaryWriter = GetBinaryWriter();
			binaryWriter.Write((byte)ConstVariable.Disconnect);
			binaryWriter.Write(clientId);
			binaryWriter.Write(LocalClientId);
			var memoryStream = (MemoryStream)binaryWriter.BaseStream;
			var fullData = memoryStream.ToArray();
			ReturnBinaryWriter(binaryWriter);
			SendData(fullData,DeliveryMethod.ReliableOrdered);
		}

		void OnGUI()
		{
			if (_relayServerPeer != null)
			{
				GUILayout.Space(200);
				GUILayout.Label($"RTT: {GetCurrentRtt(0)}");
			}
		}

		public override ulong GetCurrentRtt(ulong clientId)
		{
			try
			{
				if (_relayServerPeer == null)
					return 0;

				return (ulong)_relayServerPeer.Ping;
			}
			catch (Exception e)
			{
				LogError($"获取RTT时出错: {e.Message}");
			}

			return 0;
		}

		public override void Shutdown()
		{
			if (!_isRunning)
				return;

			LogInfo("关闭网络传输");
			try
			{
				_isRunning = false;

				// 停止LiteNetLib
				if (_netManager != null)
				{
					// 取消注册事件
					if (_listener != null)
					{
						_listener.PeerConnectedEvent -= OnPeerConnected;
						_listener.PeerDisconnectedEvent -= OnPeerDisconnected;
						_listener.NetworkReceiveEvent -= OnNetworkReceive;
					}

					// 停止网络管理器
					_netManager.Stop();
					_netManager = null;
				}

				// 清理资源
				_listener = null;
				_relayServerPeer = null;
				_clients.Clear();
				_peerIdToClientId.Clear();

				LogInfo($"{(_isServer ? "服务器" : "客户端")} 已关闭");
			}
			catch (Exception e)
			{
				LogError($"关闭时出错: {e.Message}");
			}
		}

		public override void Send(ulong clientId, ArraySegment<byte> payload, NetworkDelivery networkDelivery)
		{
			//消息包结构不使用json，而是使用byte[]，双端读取和写入的时候都按序读取，节省数据转换效率
			//消息包结构：
			// 1. 消息类型
			// 2. 消息内容
			try
			{
				ulong srcClientId = 0;
				if (_isServer)
				{
					srcClientId = ServerClientId;
					// 服务器模式：发送到指定客户端
					if (!_clients.TryGetValue(clientId, out string targetPeerString))
					{
						LogWarning($"尝试发送数据到不存在的客户端 {clientId}");
						return;
					}
				}
				else
				{
					// 客户端模式：发送到服务器
					if (clientId != ServerClientId)
					{
						LogWarning($"客户端只能发送数据到服务器 (ServerClientId: {ServerClientId})，而不是 {clientId}");
						return;
					}
					srcClientId = LocalClientId;
				}


				BinaryWriter binaryWriter = GetBinaryWriter();
				binaryWriter.Write((byte)ConstVariable.Data);
				binaryWriter.Write(clientId);
				binaryWriter.Write(srcClientId);
				binaryWriter.Write(payload.Array, payload.Offset, payload.Count);

				var memoryStream = (MemoryStream)binaryWriter.BaseStream;
				var fullData = memoryStream.ToArray();
				ReturnBinaryWriter(binaryWriter);

				var method = ConvertDeliveryMethod(networkDelivery);
				SendData(fullData,method);

				_sentPackets++;

				if (EnableVerboseLogging)
				{
					LogVerbose($"已发送 {payload.Count} 字节到 {(_isServer ? $"客户端 {clientId}" : "服务器")}");
				}
			}
			catch (Exception e)
			{
				LogError($"发送数据时出错: {e.Message}");
			}
		}

		// 将Unity Netcode的NetworkDelivery转换为LiteNetLib的DeliveryMethod
		private DeliveryMethod ConvertDeliveryMethod(NetworkDelivery networkDelivery)
		{
			switch (networkDelivery)
			{
				case NetworkDelivery.Reliable:
					return DeliveryMethod.ReliableOrdered;

				case NetworkDelivery.ReliableSequenced:
					return DeliveryMethod.ReliableOrdered;

				case NetworkDelivery.ReliableFragmentedSequenced:
					return DeliveryMethod.ReliableOrdered;

				case NetworkDelivery.Unreliable:
					return DeliveryMethod.Unreliable;

				case NetworkDelivery.UnreliableSequenced:
					return DeliveryMethod.Sequenced;

				default:
					return DeliveryMethod.ReliableOrdered;
			}
		}

		private void QueueTransportEvent(NetworkEvent type, ulong clientId, ArraySegment<byte> data)
		{
			var transportEvent = new TransportEvent
			{
				Type = type,
				ClientId = clientId,
				Data = data,
				Time = Time.realtimeSinceStartup
			};

			lock (_eventLock)
			{
				_eventQueue.Enqueue(transportEvent);
			}
		}

		private void LogInfo(string message)
		{
			Debug.Log($"[RelayNetworkTransport] {message}");
		}

		private void LogWarning(string message)
		{
			Debug.LogWarning($"[RelayNetworkTransport] {message}");
		}

		private void LogError(string message)
		{
			Debug.LogError($"[RelayNetworkTransport] {message}");
		}

		private void LogVerbose(string message)
		{
			if (EnableVerboseLogging)
			{
				Debug.Log($"[RelayNetworkTransport详细] {message}");
			}
		}

		private Queue<BinaryReader> readerPool = new Queue<BinaryReader>();
		private Queue<BinaryWriter> writerPool = new Queue<BinaryWriter>();

		BinaryReader GetBinaryReader()
		{
			if (readerPool.Count > 0)
			{
				return readerPool.Dequeue();
			}

			return new BinaryReader(new MemoryStream());
		}

		BinaryWriter GetBinaryWriter()
		{
			if (writerPool.Count > 0)
			{
				return writerPool.Dequeue();
			}

			return new BinaryWriter(new MemoryStream());
		}

		void ReturnBinaryReader(BinaryReader reader)
		{
			reader.BaseStream.SetLength(0);
			reader.BaseStream.Position = 0;
			readerPool.Enqueue(reader);
		}

		void ReturnBinaryWriter(BinaryWriter writer)
		{
			writer.BaseStream.SetLength(0);
			writer.BaseStream.Position = 0;
			writerPool.Enqueue(writer);
		}
	}
}